/*
   CodeGenerator.i386

      An ELF relocatable object file code generator for the intel 386 architecture

	CopyRight (c) 2007, Roberto García López
*/


//////
//
// Compile with this command
//
//    cc CodeGenerator.i386.2.cpp -lelf
//


//////
//
// Link the generated file with this command
//
//    ld -s -o generated generated.o
//


#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <libelf.h>
#include <err.h>
#include <sysexits.h>


#define UINT unsigned int


// Definition of the default string table section ".shstrtab"

	const char defaultStrTable[] = 
	{
		/* offset 00 */ '\0',  // The NULL section
		/* offset 01 */ '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', '\0',
		/* offset 11 */ '.', 's', 't', 'r', 't', 'a', 'b', '\0',
		/* offset 19 */ '.', 's', 'y', 'm', 't', 'a', 'b', '\0',
		/* offset 27 */ '.', 't', 'e', 'x', 't', '\0',
		/* offset 33 */ '.', 'b', 's', 's', '\0',
		/* offset 38 */ '.', 'd', 'a', 't', 'a', '\0',
		/* offset 44 */ '.', 'r', 'e', 'l', '.', 't', 'e', 'x', 't', '\0',
		/* offset 54 */ '.', 'c', 'o', 'm', 'm', 'e', 'n', 't', '\0'
	};

	const char defaultStrTableLen = sizeof(defaultStrTable); // Length of the "defaultStrTable" string
	const int iSectionsNumber = 9;  // The number of sections in the ELF object file

// End of "Definition of the default string table section"

// Sections offset
	const char _shstrtab_offset = 1;
	const char _strtab_offset = 11;
	const char _symtab_offset = 19;
	const char _text_offset = 27;
	const char _bss_offset = 33;
	const char _data_offset = 38;
	const char _rel_text_offset = 44;

// Index of sections within the object file
	const char _shstrtab = 1;
	const char _strtab = 2;
	const char _symtab = 3;
	const char _text = 4;
//	const char _bss = 5;
	const char _data = 5;

//----------------------------------------------------------------------------

int main()
{
	int FileDes;
	Elf *pElf;
	Elf32_Ehdr *pEhdr;
	Elf32_Shdr *pShdr;
	Elf_Scn *pScn;
	Elf_Data *pData;

	// Create the ELF header
		if (elf_version(EV_CURRENT) == EV_NONE) // It must appear before "elf_begin()"
			errx(EX_SOFTWARE, "ELF library initialization failed: %s", elf_errmsg(-1));

		if ((FileDes = open("generated.o", O_CREAT | O_WRONLY | O_TRUNC, 0777)) < 0)
			errx(EX_OSERR, "open \"%s\" failed", "compiled.o");

		if ((pElf = elf_begin(FileDes, ELF_C_WRITE, NULL)) == NULL)  // 3rd argument is ignored for "ELF_C_WRITE"
			errx(EX_SOFTWARE, "elf_begin() failed: %s.", elf_errmsg(-1));

		if ((pEhdr = elf32_newehdr(pElf)) == NULL)
			errx(EX_SOFTWARE, "elf32_newehdr() failed: %s", elf_errmsg(-1));

		pEhdr->e_ident[EI_CLASS] = ELFCLASS32;  // Defined by Intel architecture
		pEhdr->e_ident[EI_DATA] = ELFDATA2LSB;  // Defined by Intel architecture
		pEhdr->e_machine = EM_386;  // Intel architecture
		pEhdr->e_type = ET_REL;   // Relocatable file (object file)
		pEhdr->e_shstrndx = _shstrtab;    // Point to the shstrtab section

	// Create the section "default section header string table (.shstrtab)"
		if ((pScn = elf_newscn(pElf)) == NULL)
			errx(EX_SOFTWARE, "elf_newdata() failed: %s.", elf_errmsg(-1));
		if ((pData = elf_newdata(pScn)) == NULL)
			errx(EX_SOFTWARE, "elf_newdata() failed: %s.", elf_errmsg(-1));

		pData->d_align = 1;
		pData->d_off = 0;
		pData->d_buf = (void *) defaultStrTable;
		pData->d_type = ELF_T_BYTE;
		pData->d_size = defaultStrTableLen;
		pData->d_version = EV_CURRENT;

		if ((pShdr = elf32_getshdr(pScn)) == NULL)
			errx(EX_SOFTWARE, "elf32_etshdr() failed: %s.", elf_errmsg(-1));

		pShdr->sh_name = _shstrtab_offset;  // Point to the name of the section
		pShdr->sh_type = SHT_STRTAB;
		pShdr->sh_flags = 0;
		pShdr->sh_entsize = 0;
	// End of section "String table"

	// Create the section ".strtab"
		if ((pScn = elf_newscn(pElf)) == NULL)
			errx(EX_SOFTWARE, "elf_newdata() failed: %s.", elf_errmsg(-1));
		if ((pData = elf_newdata(pScn)) == NULL)
			errx(EX_SOFTWARE, "elf_newdata() failed: %s.", elf_errmsg(-1));

		const char strtab[] = {0, 'g', 'e', 'n', 'e', 'r', 'a', 't', 'e', 'd', '.','x', 0, 'm', 's', 'g', 0,'_', 's', 't', 'a', 'r', 't', 0};

		pData->d_align = 1;
		pData->d_off = 0;
		pData->d_buf = (void *) strtab;
		pData->d_type = ELF_T_BYTE;
		pData->d_size = sizeof(strtab);
		pData->d_version = EV_CURRENT;

		if ((pShdr = elf32_getshdr(pScn)) == NULL)
			errx(EX_SOFTWARE, "elf32_etshdr() failed: %s.", elf_errmsg(-1));

		pShdr->sh_name = _strtab_offset;
		pShdr->sh_type = SHT_STRTAB;
		pShdr->sh_flags = 0;
		pShdr->sh_entsize = 0;
	// End of section ".strtab"

	// Create the section ".symtab"
		if ((pScn = elf_newscn(pElf)) == NULL)
			errx(EX_SOFTWARE, "elf_newdata() failed: %s.", elf_errmsg(-1));
		if ((pData = elf_newdata(pScn)) == NULL)
			errx(EX_SOFTWARE, "elf_newdata() failed: %s.", elf_errmsg(-1));

		Elf32_Sym x[6];

		// Definition of the undefined section (this must be the first item by the definition of TIS ELF)
			x[0].st_name = 0;
			x[0].st_value = 0;
			x[0].st_size = 0;
			x[0].st_info = 0;
			x[0].st_other = 0;
			x[0].st_shndx = SHN_UNDEF;

		// Definition of the name of the source file (this must be the second item by the definition in TIS ELF)
			x[1].st_name = 1;
			x[1].st_value = 0;
			x[1].st_size = 0;
			x[1].st_info = ELF32_ST_INFO(STB_LOCAL, STT_FILE); // This is the value that st_info must have (because of TIS ELF)
			x[1].st_other = 0;
			x[1].st_shndx = SHN_ABS;  // The section where the symbol is
/*
		// Definition of the absolute section (That's what nasm does. But I don't know why)
			x[2].st_name = 0;
			x[2].st_value = 0;
			x[2].st_size = 0;
			x[2].st_info = ELF32_ST_INFO(STB_LOCAL, STT_SECTION);
			x[2].st_other = 0;
			x[2].st_shndx = SHN_ABS;  // The section where the symbol is
*/
		// Definition of the ".text" section as a section in the ".symtab" section

			// I don't know why this section is declared here because there's no reference to it. But "nasm" and "cc" define it

			x[2].st_name = 0;
			x[2].st_value = 0;
			x[2].st_size = 0;
			x[2].st_info = ELF32_ST_INFO(STB_LOCAL, STT_SECTION);
			x[2].st_other = 0;
			x[2].st_shndx = _text;  // The section where the symbol is

		// Definition of the ".data" section as a section in the ".symtab" section
			x[3].st_name = 0;
			x[3].st_value = 0;
			x[3].st_size = 0;
			x[3].st_info = ELF32_ST_INFO(STB_LOCAL, STT_SECTION);
			x[3].st_other = 0;
			x[3].st_shndx = _data;  // The section where the symbol is

		// Definition of the "msg" symbol
			x[4].st_name = 13;  // Offset in the "strtab" section where the name start
			x[4].st_value = 0;
			x[4].st_size = 0;
			x[4].st_info = ELF32_ST_INFO(STB_LOCAL, STT_NOTYPE);
			x[4].st_other = 0;
			x[4].st_shndx = _data;  // The section where the symbol is

		// Definition of the "_start" symbol
			x[5].st_name = 17;  // Offset in the "strtab" section where the name start
			x[5].st_value = 0;
			x[5].st_size = 0;
			x[5].st_info = ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE);
			x[5].st_other = 0;
			x[5].st_shndx = _text;  // The section where the symbol is

		pData->d_align = 4;  // nasm define it as 4
		pData->d_off = 0;
		pData->d_buf = (void *) x;
		pData->d_type = ELF_T_BYTE;
		pData->d_size = sizeof(x);
		pData->d_version = EV_CURRENT;

		if ((pShdr = elf32_getshdr(pScn)) == NULL)
			errx(EX_SOFTWARE, "elf32_etshdr() failed: %s.", elf_errmsg(-1));

		pShdr->sh_name = _symtab_offset;  // Point to the name of the section
		pShdr->sh_type = SHT_SYMTAB;
		pShdr->sh_flags = 0;
		pShdr->sh_link = _strtab;  // point to the section .strtab (the section that contain the strings)
		pShdr->sh_info = ELF32_ST_INFO(STB_LOCAL, 5);  // the second argument is beause of TIS ELF (One greater than the symbol table index of the las local symbol (binding STB_LOCAL))
		pShdr->sh_entsize = sizeof(Elf32_Sym); // sizeof(Elf32_Sym) because of TIS ELF for fixed-size symbol table.
	// End of section ".symtab"

	// Create the section ".text"
		if ((pScn = elf_newscn(pElf)) == NULL)
			errx(EX_SOFTWARE, "elf_newdata() failed: %s.", elf_errmsg(-1));
		if ((pData = elf_newdata(pScn)) == NULL)
			errx(EX_SOFTWARE, "elf_newdata() failed: %s.", elf_errmsg(-1));

		pData->d_align = 4;  // "nasm" defines it as 16 and "cc" as 4
		pData->d_off = 0;
		// This is temporal
			const char assemblerinstructions[] = {0xb9, 0, 0, 0, 0, 0xba, 0x0c, 0, 0, 0, 0xbb, 1, 0, 0, 0, 0xb8, 4, 0, 0, 0, 0xcd, 0x80, 0xb8, 1, 0, 0, 0, 0xcd, 0x80};
		pData->d_buf = (void *) assemblerinstructions;
		pData->d_type = ELF_T_BYTE;
		pData->d_size = sizeof(assemblerinstructions);
		pData->d_version = EV_CURRENT;

		if ((pShdr = elf32_getshdr(pScn)) == NULL)
			errx(EX_SOFTWARE, "elf32_etshdr() failed: %s.", elf_errmsg(-1));

		pShdr->sh_name = _text_offset;
		pShdr->sh_type = SHT_PROGBITS;
		pShdr->sh_flags = SHF_ALLOC | SHF_EXECINSTR;
		pShdr->sh_entsize = 0;
	// End of section ".text"

	// Create the section ".data"
		if ((pScn = elf_newscn(pElf)) == NULL)
			errx(EX_SOFTWARE, "elf_newdata() failed: %s.", elf_errmsg(-1));
		if ((pData = elf_newdata(pScn)) == NULL)
			errx(EX_SOFTWARE, "elf_newdata() failed: %s.", elf_errmsg(-1));

		pData->d_align = 4;  // defined by nasm I think it must vary
		pData->d_off = 0;
		// This is temporal
			const char datacontent[] = {'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0xa};
		pData->d_buf = (void *) datacontent;
		pData->d_type = ELF_T_BYTE;
		pData->d_size = sizeof(datacontent);
		pData->d_version = EV_CURRENT;

		if ((pShdr = elf32_getshdr(pScn)) == NULL)
			errx(EX_SOFTWARE, "elf32_etshdr() failed: %s.", elf_errmsg(-1));

		pShdr->sh_name = _data_offset;
		pShdr->sh_type = SHT_PROGBITS;
		pShdr->sh_flags = SHF_ALLOC | SHF_WRITE;
		pShdr->sh_entsize = 0;
	// End of section ".data"

	// Create the section ".rel.text"
		if ((pScn = elf_newscn(pElf)) == NULL)
			errx(EX_SOFTWARE, "elf_newdata() failed: %s.", elf_errmsg(-1));
		if ((pData = elf_newdata(pScn)) == NULL)
			errx(EX_SOFTWARE, "elf_newdata() failed: %s.", elf_errmsg(-1));

		pData->d_align = 4;
		pData->d_off = 0;
		// This is temporal
			Elf32_Rel rel[1];

			rel[0].r_offset = 1;
			rel[0].r_info = ELF32_R_INFO(3, R_386_32);  // 3 is the index in the symbol table where the symbol is (.data section)

		pData->d_buf = (void *) rel;
		pData->d_type = 0; // The type of each relocation is specified in the relocation itself
		pData->d_size = sizeof(rel);
		pData->d_version = EV_CURRENT;

		if ((pShdr = elf32_getshdr(pScn)) == NULL)
			errx(EX_SOFTWARE, "elf32_etshdr() failed: %s.", elf_errmsg(-1));

		pShdr->sh_name = _rel_text_offset;
		pShdr->sh_type = SHT_REL;
		pShdr->sh_flags = 0;
		pShdr->sh_link = _symtab;
		pShdr->sh_info = _text;
		pShdr->sh_entsize = 0;
	// End of section ".rel.text"

	// Update the sections internally
		if (elf_update(pElf, ELF_C_NULL) < 0)
			errx(EX_SOFTWARE, "elf_update(NULL) failed: %s.", elf_errmsg(-1));

	// Write the object file
		if (elf_update(pElf, ELF_C_WRITE) < 0)
			errx(EX_SOFTWARE, "elf_update() failed: %s.", elf_errmsg(-1));

	// Close all handles
		elf_end(pElf);
		close(FileDes);

	return 0;
}
